<?php
/**
 *
 * Filesystem based cache, for shared directory between clustered servers
 * @author Fernando Palomo Garcia fernando@kernelius.com
 * @version 0.1
 * @license Apache License 2.0
 */
class Kernelius_Cache_Persistent_Filesystem extends Kernelius_Cache_Storage
implements Kernelius_Cache_Persistent
{

    /**
     * base directory to save sesion files
     */
    const BASE_DIR="/data/sessions";

    /**
     * echo command
     */
    const CMD_ECHO="/bin/echo";

    /**
     * touch command
     */
    const CMD_TOUCH="/bin/touch";

    /**
     * rm command
     */
    const CMD_RM="/bin/rm";


    /**
     * find command
     */
    const CMD_FIND="/usr/bin/find";

    /**
     * directory level to create
     */
    const MAX_RECURSIVE_DIR=16;

    public function _construct(){
        // check availability for base dir
        if(!file_exists(self::BASE_DIR)){
            mkdir(self::BASE_DIR);
        }
    }


    /**
     * create route for this key
     * @param string $key
     */
    protected function createFilePath($key){
        $path=self::BASE_DIR."/";
        if(!file_exists($this->findPath($key))){
            for($i=0;$i<=self::MAX_RECURSIVE_DIR;$i++){
                $path.= substr($key,$i,1)."/";
                if(!file_exists($path)){
                    mkdir($path); // or Kernelius_Logger::log("ERROR CREATING $path");
                }
            }
        }
    }


    /**
     * get path for this key
     * @param string $key
     * @return string 
     */
    protected function findPath($key){
        $path= self::BASE_DIR. "/";
        for( $i=0; $i<=self::MAX_RECURSIVE_DIR; $i++){
            $path.= substr($key,$i,1). "/";
        }
        return $path.$key;
    }


    /**
     * set value
     * //TODO check if key was saved correctly
     * @param  $key
     * @param  $value
     * @param  $timeout
     * @return true
     */
    public function set($key, $value, $timeout=Kernelius_Cache::DEFAULT_EXPIRY){
        $wrapped_key=$this->wrap($key);
        $this->createFilePath($wrapped_key);
        $file= $this->findPath($wrapped_key);
        Kernelius_Logger::log("writing $file to disk");
        $cmd=self::CMD_ECHO ." ".escapeshellarg($value)." > {$file} & ";
        pclose(popen($cmd,"r"));
        return true;
    }


    /**
     *  get key
     * @param string $key
     * @return object
     */
    public function get($key){
        $wrapped_key= $this->wrap($key);
        $file= $this->findPath($wrapped_key);
        Kernelius_Logger::log("Reading from disk $file");
        return file_get_contents($file);
    }


    /**
     * delete key
     * @param string $key
     */
    public function delete($key){
        $wrapped_key= $this->wrap($key);
        $file= $this->findPath($wrapped_key);
        Kernelius_Logger::log("Removing $file");
        pclose(popen(self::CMD_RM." -f {$file} &","r"));
    }


    /**
     * touch file , refresh mtime
     * @param string $key
     */
    public function touch($key){
        $wrapped_key= $this->wrap($key);
        $file= $this->findPath($wrapped_key);
        Kernelius_Logger::log("touching $file");
        pclose(popen(self::CMD_TOUCH." {$file} &","r"));
    }


    /**
     * delete files older than $older minutes
     * @param int $older
     */
    public function gc($older){
        pclose(popen( self::CMD_FIND." ". self::BASE_DIR ." -mmin +".$older. "&","r"));
        
    }


}